package plgbase

import (
	"dgo/goutils/utils"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/gobase/factory"
	"gitee.com/ymofen/gobase/subpub"
	"gitee.com/ymofen/supervalue"
	"io"
	"sync/atomic"
)

type BasePubNode struct {
	pubSessionPrefix string
	pubsessionid     string
	pubconnstr       string
	stepMsg          string
	errmsg           string

	pubTemplate string
	pubTopic    string

	conf      gobase.StrMap
	objectRef *gobase.ObjectInnerRef
	sub       subpub.ISubA
	pub       subpub.IPub
}

var (
	pubnodesessionN int32
)

// subprefix 创建session时的前缀
func NewBasePubNode(subprefix string) *BasePubNode {
	rval := &BasePubNode{pubSessionPrefix: subprefix}
	return rval
}

func (this *BasePubNode) GetSubSessionID() string {
	return this.pubsessionid
}

func (this *BasePubNode) ConfString() string {
	return this.pubconnstr
}

func (this *BasePubNode) StatusText() string {
	return fmt.Sprintf("%s, step:%s", this.ConfString(), this.stepMsg)
}

func (this *BasePubNode) closePub() {
	if this.objectRef != nil {
		this.stepMsg = fmt.Sprintf("[%s]closePub->ReleaseRef:0", utils.NowString())
		sub := this.sub
		if sub != nil {
			sub.Unsub(this.pubsessionid, "evt")
			sub.Unsub(this.pubsessionid, "evt-pub")
			this.sub = nil
		}

		this.objectRef.ReleaseRef()
		this.pub = nil
		this.stepMsg = fmt.Sprintf("[%s]closePub->ReleaseRef:1", utils.NowString())
		this.pubsessionid = ""
		this.pubconnstr = ""
	}
}

func (this *BasePubNode) Close() error {
	if len(this.pubsessionid) == 0 {
		return io.ErrClosedPipe
	}
	this.closePub()
	return nil
}

func (this *BasePubNode) GetStepMsg() string {
	return this.stepMsg
}

func (this *BasePubNode) ReloadConnConfString(str string, notify subpub.SubFunc) error {
	if str != this.pubconnstr {
		confMap := gobase.NewStrMap()
		this.closePub()
		this.pubconnstr = str
		confMap.URLFormDecode(str)

		this.pubTopic = confMap["topic"]
		this.pubTemplate = confMap["templ"]

		this.checkPubNode(confMap, notify)
	}
	return nil
}

func (this *BasePubNode) checkGetObj(conf gobase.StrMap) (*gobase.ObjectInnerRef, error) {
	conntype := conf.StringByName("conntype", "")
	conntype = fmt.Sprintf("subnode.%s", conntype)
	obj, err := factory.CreateInstance(conntype, conf)
	if err != nil {
		return nil, err
	}

	if lvintf, ok := obj.(*gobase.ObjectInnerRef); ok {
		return lvintf, nil
	} else {
		return nil, fmt.Errorf("%s不是gobase.ObjectInnerRef对象", conntype)
	}
}

func (this *BasePubNode) Pub(topic string, max int, args ...interface{}) int {
	pub := this.pub
	if pub == nil {
		return -1
	}
	if len(topic) == 0 {
		topic = this.pubTopic
	}

	return pub.Pub(topic, max, args...)
}

func (this *BasePubNode) CreateTemplateJSON(cache bool) *wrapper.SuperValue {
	if len(this.pubTemplate) == 0 {
		return wrapper.NewSVObjectCache(cache)
	}
	json, _ := wrapper.NewSVFromBufCache([]byte(this.pubTemplate), cache)
	if json == nil {
		return wrapper.NewSVObjectCache(cache)
	} else {
		return json
	}
}

func (this *BasePubNode) checkPubNode(confMap gobase.StrMap, notify subpub.SubFunc) error {
	if len(this.pubsessionid) == 0 {
		this.pubsessionid = fmt.Sprintf("%s-pub-%d", this.pubSessionPrefix, atomic.AddInt32(&pubnodesessionN, 1))
	}
	this.stepMsg = fmt.Sprintf("[%s]checkPubNode->checkGetObj:0", utils.NowString())
	objref, err := this.checkGetObj(confMap)
	if err != nil {
		this.stepMsg = fmt.Sprintf("[%s]checkPubNode->checkGetObj:-1, err:%s", utils.NowString(), err.Error())
		return err
	} else {
		this.stepMsg = fmt.Sprintf("[%s]checkPubNode->checkGetObj:1", utils.NowString())
		this.objectRef = objref
		if notify != nil {
			if lvintf, ok := objref.GetObject().(subpub.ISubA); ok {
				lvintf.Sub(this.pubsessionid, "evt-pub", notify)
				lvintf.Sub(this.pubsessionid, "evt", notify)
				this.sub = lvintf
			}
		}
		this.pub = objref.GetObject().(subpub.IPub)

		err = this.objectRef.CheckStart()
		if err == gobase.ErrUnsupported {
			this.stepMsg = fmt.Sprintf("[%s]checkPubNode->checkGetObj:2, checkstart unsupoort!", utils.NowString())
		} else {
			this.stepMsg = fmt.Sprintf("[%s]checkPubNode->checkGetObj:2", utils.NowString())
		}
		return err
	}
}

func (this *BasePubNode) ReloadConf(conf *wrapper.SuperValue, notifyfn subpub.SubFunc) error {
	if conf == nil {
		this.closePub()
		return nil
	}

	confMap := gobase.NewStrMap()
	str := conf.String()
	if str != this.pubconnstr {
		this.closePub()
		this.pubconnstr = str
		if conf.IsString() {
			confMap.URLFormDecode(str)
		} else if conf.IsObject() {
			url := conf.StringByName("url", "")
			if len(url) > 0 {
				confMap.URLFormDecode(url)
			}
			conf.Range(func(key string, val *wrapper.SuperValue) bool {
				confMap[key] = val.String()
				return true
			})
		}

		this.pubTopic = confMap["topic"]

		this.pubTemplate = confMap["templ"]

		return this.checkPubNode(confMap, notifyfn)
	}
	return nil
}
